package com.to8to.kitt;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import org.apache.thrift.TApplicationException;
import org.apache.thrift.TBase;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TMessageMeta;
import org.apache.thrift.protocol.TMessageType;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.to8to.kitt.esb.ServiceMethodObj;
import com.to8to.kitt.utils.LogUtils;

public class ThriftCodec {

//	public static Logger logger = LoggerFactory.getLogger(ThriftCodec.class);  
	 
	public static int initBuffSize = 64;
	 
	public static ByteBuf encode(ThriftMessage msg)
	{
		ByteBuf out = Unpooled.buffer(initBuffSize);
		
		TTransport transport = new TNettyTransport(null, out);
		TProtocol protocol = ThriftClassFactory.ProtocolFactory.getProtocol(transport);
		try {
			protocol.writeMessageBegin(msg.getHead());
			if (msg.getHead().type == TMessageType.CALL 
					|| msg.getHead().type == TMessageType.REPLY)
				msg.getBody().write(protocol);
			else
				msg.getException().write(protocol);
			protocol.writeMessageEnd();
			protocol.getTransport().flush();
			return out;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	public static ThriftMessage decode(ByteBuf in) 
	{	
		TTransport transport = new TNettyTransport(null, in);
		
		TProtocol protocol = ThriftClassFactory.ProtocolFactory.getProtocol(transport);
		
		ThriftMessage msg = null;
		
		try {
			TMessage head = protocol.readMessageBegin();
			
			TMessageMeta meta = new TMessageMeta(head.name);
			
			LogUtils.openTag(meta.tag);
			
			String method = meta.method;
			
			String service = meta.service;
			
			ServiceMethodObj sm = ThriftClassFactory.getServiceMethodObj(service);
			
			if(sm == null && ThriftClassFactory.ServiceMethodMap.size() > 0)
				sm = ThriftClassFactory.ServiceMethodMap.values().iterator().next();
			
			byte type = head.type;
			if (type == TMessageType.CALL || type == TMessageType.REPLY) 
			{
				TBase<?,?> body = null;
				if (type == TMessageType.CALL)
				{
					TBase<?,?> tmp = sm.argsMap.get(method);
					if(tmp != null)
						body = tmp.deepCopy();
				}
				else
				{
					TBase<?,?> tmp = sm.resultMap.get(method);
					if(tmp != null)
						body = tmp.deepCopy();
				}
				
				if(body != null)
				{
					body.read(protocol);
					protocol.readMessageEnd();
					msg = new ThriftMessage(head, body);
					
				}
				else 
				{
//					logger.error("Unknown method: " + method + ", for service: " + sm.serviceClass.getName());
					TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, "Unknown method: " + method + ", for service: " + sm.serviceClass.getName());
					msg = new ThriftMessage(head, x);
				}
			} 
			else if (type == TMessageType.EXCEPTION)
			{
				TApplicationException x = TApplicationException.read(protocol);
				protocol.readMessageEnd();
				msg = new ThriftMessage(head, x);
			}
			else
			{
//				logger.error("Invalid message type: " + type);
				TApplicationException x = new TApplicationException(TApplicationException.INVALID_MESSAGE_TYPE, "Invalid message type: " + type);
				msg = new ThriftMessage(head, x);
			}
			
			msg.setMeta(meta);
			
		} catch (Exception e) {
//			logger.error("", e);
		}
		
		return msg;
	}
	
	public static void decode(byte[] bytes, TBase<?,?> tbase)
	{
		System.out.println("decode start ...." + System.currentTimeMillis());
		long t1 = System.currentTimeMillis();
		ByteBuf in = Unpooled.wrappedBuffer(bytes);
		long t2 = System.currentTimeMillis();
		
		TTransport transport = new TNettyTransport(null, in);
		
		TProtocol protocol = ThriftClassFactory.ProtocolFactory.getProtocol(transport);
		
		long t3 = System.currentTimeMillis();
		System.out.println("wrap:" + (t2 - t1));
		System.out.println("xxxx:" + (t3 - t2));
		
		try {
			
			tbase.read(protocol);
			long t4 = System.currentTimeMillis();
			System.out.println("read:" + (t4 - t3));
			
		} catch (Exception e) {
//			logger.error("Exception:{}\n{}", e, LogUtils.logStackTrace(e));
		}
	}
	
	public static byte[] encode(TBase<?,?> tbase)
	{
		ByteBuf out = Unpooled.buffer(initBuffSize);
		
		TTransport transport = new TNettyTransport(null, out);
		
		TProtocol protocol = ThriftClassFactory.ProtocolFactory.getProtocol(transport);
		
		try {
			
			tbase.write(protocol);
			
			protocol.getTransport().flush();
			
			return out.array();
			
		} catch (Exception e) {
			out.clear();
//			logger.error("Exception:{}\n{}", e, LogUtils.logStackTrace(e));
		}
		
		return null;
	}
	
	
	
	public static void main(String[] args)
	{
		
	}
}
